/**
 ******************************************************************************
 * @file    bsp_usart.c
 * @version V1.0
 * @date    2013-xx-xx
 * @brief   �����õ�printf���ڣ��ض���printf������
 ******************************************************************************
 * @attention
 *
 * ʵ��ƽ̨:Ұ�� F103-ָ���� STM32 ������
 * ��̳    :http://www.firebbs.cn
 * �Ա�    :https://fire-stm32.taobao.com
 *
 ******************************************************************************
 */
#include <string.h>
#include <stdio.h>
#include "./BSP/bsp_usart.h"

/*
	创建一个存储了所有端口配置的结构体数组
*/

// 各USART属性结构体 数组
static USART_PropertyTypeDef usarts[COM_MAX] = {
	{.USART_GPIO_CLK = RCC_APB2Periph_GPIOA, .rxd_port = GPIOA, .rxd_pin = GPIO_Pin_10, .txd_port = GPIOA, .txd_pin = GPIO_Pin_9, .USART_CLK = RCC_APB2Periph_USART1, .USARTx = USART1, .USARTx_IRQn = USART1_IRQn},
	{.USART_GPIO_CLK = RCC_APB2Periph_GPIOA, .rxd_port = GPIOA, .rxd_pin = GPIO_Pin_3, .txd_port = GPIOA, .txd_pin = GPIO_Pin_2, .USART_CLK = RCC_APB1Periph_USART2, .USARTx = USART2, .USARTx_IRQn = USART2_IRQn},
	{.USART_GPIO_CLK = RCC_APB2Periph_GPIOB, .rxd_port = GPIOB, .rxd_pin = GPIO_Pin_11, .txd_port = GPIOB, .txd_pin = GPIO_Pin_10, .USART_CLK = RCC_APB1Periph_USART3, .USARTx = USART3, .USARTx_IRQn = USART3_IRQn},
	{.USART_GPIO_CLK = RCC_APB2Periph_GPIOC, .rxd_port = GPIOC, .rxd_pin = GPIO_Pin_11, .txd_port = GPIOC, .txd_pin = GPIO_Pin_10, .USART_CLK = RCC_APB1Periph_UART4, .USARTx = UART4, .USARTx_IRQn = UART4_IRQn},
	{.USART_GPIO_CLK = RCC_APB2Periph_GPIOD | RCC_APB2Periph_GPIOC, .rxd_port = GPIOD, .rxd_pin = GPIO_Pin_2, .txd_port = GPIOC, .txd_pin = GPIO_Pin_12, .USART_CLK = RCC_APB1Periph_UART5, .USARTx = UART5, .USARTx_IRQn = UART5_IRQn},
};

/*
	静态函数：USART配置函数
	参数：Property 串口属性结构体，USART_InitStructure 串口初始化结构体，NVIC_InitStructure 嵌套向量中断控制器初始化结构体
*/
static void USART_Config(USART_PropertyTypeDef *Property, USART_InitTypeDef *USART_InitStructure, NVIC_InitTypeDef *NVIC_InitStructure)
{
	GPIO_InitTypeDef GPIO_InitStructure;

	RCC_APB2PeriphClockCmd(Property->USART_GPIO_CLK, ENABLE); // 打开串口GPIO的时钟
	// 打开串口外设的时钟，只有串口1在APB2时钟域，其他都在APB1时钟域
	if (Property->USARTx == USART1)
		RCC_APB2PeriphClockCmd(Property->USART_CLK, ENABLE);
	else
		RCC_APB1PeriphClockCmd(Property->USART_CLK, ENABLE);

	// 配置USART Tx的GPIO为复用推挽模式，并设置为输出模式，速度为50MHz
	GPIO_InitStructure.GPIO_Pin = Property->txd_pin;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;		// 设置复用推挽输出模式
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;	// 设置GPIO速度为50MHz
	GPIO_Init(Property->txd_port, &GPIO_InitStructure); // 配置发送GPIO
	// 配置USART Rx的GPIO为浮空输入模式
	GPIO_InitStructure.GPIO_Pin = Property->rxd_pin;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; // 设置浮空输入模式
	GPIO_Init(Property->rxd_port, &GPIO_InitStructure);	  // 配置接收GPIO

	// 配置USART的工作参数
	USART_Init(Property->USARTx, USART_InitStructure);

	// 配置USART的NVIC
	NVIC_Init(NVIC_InitStructure);

	// 使能USART的接收中断
	USART_ITConfig(Property->USARTx, USART_IT_RXNE, ENABLE);
	// 使能串口外设
	USART_Cmd(Property->USARTx, ENABLE);
}

/*
	简介：初始化指定串口的接口函数
	参数：comx 串口号，baud 波特率
*/
void SerialPortInit(COM_Enum comx, u32 baud)
{
	if (comx >= COM_MAX)
		return; // 错误处理：索引越界

	USART_PropertyTypeDef Property = usarts[comx]; // 将串口配置结构体数组对应串口的的值赋值给Property,让其作为参数传入USART_Config函数
	USART_InitTypeDef USART_InitStructure = {
		.USART_BaudRate = baud,
		.USART_WordLength = USART_WordLength_8b,					 // 配置帧长为8位数据
		.USART_StopBits = USART_StopBits_1,							 // 配置停止位为1位
		.USART_Parity = USART_Parity_No,							 // 配置无校验位
		.USART_HardwareFlowControl = USART_HardwareFlowControl_None, // 配置硬件流控制为无硬件流控制
		.USART_Mode = USART_Mode_Rx | USART_Mode_Tx					 // 配置工作模式为收发一起
	};
	// 配置USART为中断源
	NVIC_InitTypeDef NVIC_InitStructure = {
		.NVIC_IRQChannel = Property.USARTx_IRQn,
		.NVIC_IRQChannelPreemptionPriority = 1, // 抢断优先级
		.NVIC_IRQChannelSubPriority = 1,		// 子优先级
		.NVIC_IRQChannelCmd = ENABLE			// 使能中断
	};
	// 调用静态函数USART_Config进行USART初始化配置
	USART_Config(&Property, &USART_InitStructure, &NVIC_InitStructure);
}

static char usart1_recv_buf[128];
static char usart2_recv_buf[128];
static char usart3_recv_buf[128];
static char usart4_recv_buf[128];
static char usart5_recv_buf[128];
static UsartRecvMsgType usart_msg[COM_MAX] = {
	{.finish = 0, .index = 0, .buf = usart1_recv_buf, .buf_size = 128},
	{.finish = 0, .index = 0, .buf = usart2_recv_buf, .buf_size = 128},
	{.finish = 0, .index = 0, .buf = usart3_recv_buf, .buf_size = 128},
	{.finish = 0, .index = 0, .buf = usart4_recv_buf, .buf_size = 128},
	{.finish = 0, .index = 0, .buf = usart5_recv_buf, .buf_size = 128},
};

/*
从指定串口接收数据，并存入对应串口的缓存区
*/
u16 usart_get_msg(COM_Enum comx, char *mem, u16 len)
{
	u16 size = 0;
	if (usart_msg[comx].finish == 1)
	{
		size = len > usart_msg[comx].index ? usart_msg[comx].index : len;
		memcpy(mem, usart_msg[comx].buf, size);
	}
	return size;
}
/*
	向指定串口发送字符串
*/
void usart_send_msg(COM_Enum comx, const char *msg)
{
	while (*msg != '\0')
	{
		/* 发送一个字节的数据 */
		USART_SendData(usarts[comx].USARTx, (uint8_t)*msg);

		/* 等待发送完毕 */
		while (USART_GetFlagStatus(usarts[comx].USARTx, USART_FLAG_TXE) == RESET)
			;
		msg++;
	}
}

/*
	清除接收到的数据，软重置，并没有清空对应的缓存区，
	重置完成标志和位置标记
*/
void usart_clear_msg(COM_Enum comx)
{
	usart_msg[comx].finish = 0;
	usart_msg[comx].index = 0;
}

/*
	中断时使用的串口接收寄存器读取一个字符，存到对应端口的接收缓存区，
	字符串接收函数，接收到换行符时，标志位finish置1，
	接收到的数据存入缓存区，并更新位置标记
*/
void usart_receive_str(COM_Enum comx, u8 c)
{
	if (c == '\n')
		usart_msg[comx].finish = 1;

	if (usart_msg[comx].index < usart_msg[comx].buf_size - 1)
		usart_msg[comx].buf[usart_msg[comx].index++] = c;
}

void USART_Puts(USART_TypeDef *usart, const char *msg)
{
	while (*msg != '\0')
	{
		/* 发送一个字节数据到串口 */
		USART_SendData(usart, (uint8_t)*msg);

		/* 等待发送完毕 */
		while (USART_GetFlagStatus(usart, USART_FLAG_TXE) == RESET)
			;
		msg++;
	}
}

// 重定向c库函数printf到串口，重定向后可使用printf函数
int fputc(int ch, FILE *f)
{
	/* 发送一个字节数据到串口 */
	USART_SendData(DEBUG_USARTx, (uint8_t)ch);

	/* 等待发送完毕 */
	while (USART_GetFlagStatus(DEBUG_USARTx, USART_FLAG_TXE) == RESET)
		;

	return (ch);
}

// 重定向c库函数scanf到串口，重定向后可使用scanf函数
int fgetc(FILE *f)
{
	/* 等待串口输入数据 */
	while (USART_GetFlagStatus(DEBUG_USARTx, USART_FLAG_RXNE) == RESET)
		;
	return (int)USART_ReceiveData(DEBUG_USARTx);
}
